"""
本文件定义PyTimetableCompression程序的数据结构，并实现csv格式文件数据读写功能 
"""

import csv
import datetime

from xml.etree.ElementTree import Element
from xml.etree.ElementTree import SubElement
from xml.etree.ElementTree import ElementTree

import os


class PTCData:
    ''' 数据 '''

    # 参数
    TimetableStartTime = 330
    ''' 压缩运行图的开始时刻 '''

    TimetableEndTime = 1439
    ''' 压缩运行图的结束时刻 '''

    # 集合
    StationList = list()
    ''' 车站列表 '''

    SegmentList = list()
    ''' 区间列表 '''

    TrainList = list()
    ''' 列车列表 '''

    def WriteTimetable(self):
        ''' 输出运行图数据 '''

        f = open("./OutputFiles/CompressedTimetable.csv",
                 "w", encoding="utf-8")
        f.writelines("TrainID,StationID,StationName,EventType,Time\n")

        for train in self.TrainList:
            for event in train.CompressedTrainEventList:
                HH = int(event.Time / 60)
                mm = (event.Time - HH * 60)
                timeStr = datetime.time(HH, mm, 00)
                f.writelines("{0},{1},{2},{3},{4}\n".format(
                    train.ID, event.Station.ID, event.Station.Name, event.Type, timeStr))
        f.close()


class Station:
    ''' 车站 '''

    ID = ""
    ''' 车站ID '''

    Name = ""
    ''' 车站名 '''

    Miles = -1
    ''' 车站中心里程 '''


class Segment:
    '''区间类'''

    ID = ""
    ''' 区间ID '''

    Name = ""
    ''' 区间名 '''

    FromStation = None
    ''' 起始车站 '''

    ToStation = None
    ''' 中止车站 '''

    RulerDict = None
    ''' 标尺字典 '''

    HeadwayDict = None
    ''' 追踪间隔字典 '''

    def GetRunningTime(self, trainType, isAccelerate, isDecelerate):
        ''' 获取区间运行时分 '''

        ruler = self.RulerDict[trainType]  # 根据列车类型获取对应的标尺对象

        runningTime = ruler.FreeFlowRunningTime  # 纯运行时分

        if isAccelerate:
            runningTime = runningTime + ruler.AccelerateAdd  # 起动附加时分
        if isDecelerate:
            runningTime = runningTime + ruler.DecelerateAdd  # 停车附加时分

        return runningTime


    def GetHeadway(self, headwayID, operationType, isLeadingTrainStop, isFollowingTrainStop):
        ''' 根据作业类型获取间隔时间 '''

        hw = self.HeadwayDict[headwayID]  # 根据间隔时间类型获取间隔时间对象

        if operationType == "Arrival":  # 到达间隔
            if isLeadingTrainStop:  # 前车停站
                if isFollowingTrainStop:  # 后车停站
                    return hw.I_aa
                else:  # 后车不停站
                    return hw.I_ap
            else:  # 前车不停站
                if isFollowingTrainStop:  
                    return hw.I_pa
                else:
                    return hw.I_pp
        else:  # 出发间隔
            if isLeadingTrainStop:
                if isFollowingTrainStop:
                    return hw.I_dd
                else:
                    return hw.I_dp
            else:
                if isFollowingTrainStop:
                    return hw.I_pd
                else:
                    return hw.I_pp


class Train:
    ''' 列车 '''

    ID = ""
    ''' 列车ID '''

    Type = ""
    ''' 列车种类 '''

    RouteStationList = None
    ''' 经过车站列表 '''

    RouteSegmentList = None
    ''' 经过区间列表 '''

    OriginalTrainEventList = None
    ''' 初始运行图时刻表 '''

    CompressedTrainEventList = None
    ''' 压缩运行图时刻表 '''

    def GetStopTime(self, station):
        ''' 列车是否停站 '''

        stationDep = [e for e in self.OriginalTrainEventList if e.Station.ID ==
                      station.ID and e.Type == "Departure"][0]  # 获取列车在该车站的出发事件
        stationArr = [e for e in self.OriginalTrainEventList if e.Station.ID ==
                      station.ID and e.Type == "Arrival"][0]  # 获取列车在该车站的到达事件

        if stationArr.Time < stationDep.Time:  # 如果到达事件早于出发事件发生，说明原图中列车在该车站有停站
            return stationDep.Time - stationArr.Time  # 返回原图中该列车在该车站的实际的停站时间
        else:
            return 0


class TrainEvent:
    ''' 列车到发作业事件 '''

    Train = None
    ''' 所属列车 '''

    Station = None
    ''' 所属车站 '''

    Type = ""
    ''' 作业类型 '''

    Time = -1
    ''' 作业时刻 '''


class Ruler:
    """ 标尺 """

    Segment = None
    ''' 区间 '''

    TrainType = ""
    ''' 列车类型 '''

    FreeFlowRunningTime = -1
    '''  纯运行时分 '''

    AccelerateAdd = -1
    ''' 起动附加时分 '''

    DecelerateAdd = -1
    ''' 停车附加时分 '''


class StopPattern:
    """ 停站方案 """

    ID = ""
    ''' 停站方案ID '''

    Station = None
    ''' 所属车站 '''

    MinDwellTime = -1
    ''' 最小停站时分 '''


class Headway:
    """ 间隔时间 """

    ID = ""
    ''' 间隔时间ID '''

    Segment = None
    ''' 所属区间 '''

    I_aa = -1
    ''' 到达间隔 '''

    I_dd = -1
    ''' 出发间隔 '''

    I_pd = -1
    ''' 通发间隔 '''

    I_ap = -1
    ''' 到通间隔 '''

    I_pp = -1
    ''' 通通间隔 '''

    I_pa = -1
    ''' 通到间隔 '''

    I_dp = -1
    ''' 发通间隔 '''


def ConvertTimeToTotalMinute(time):
    ''' 将时间转换为总分钟数 '''

    timeArray = time.split(".")  # 利用小数点字符将输入数据分割
    HH = int(timeArray[0])  # 小时数
    if timeArray.__len__() == 1:  # 如果没有获得分钟数，则分钟数设为0
        mm = 0
    else:
        mm = (float(time) - HH) * 100  # 计算分钟数
    return HH * 60 + mm  # 返回以分为单位的时间


def LoadDataFromFiles(fileDirectory):
    ''' 从文件中读取数据 '''

    # 创建数据对象
    ptcData = PTCData()

    # 读取车站数据
    f = open("./InputFiles/Station.csv", encoding='utf-8')
    for item in csv.DictReader(f):
        station = Station()
        station.ID = item["ID"]
        station.Name = item["Name"]
        station.Miles = item["Miles"]

        ptcData.StationList.append(station)
    f.close()

    # 读取区间数据
    f = open("./InputFiles/Segment.csv", encoding='utf-8')
    for item in csv.DictReader(f):
        segment = Segment()
        segment.ID = item["ID"]
        segment.FromStation = [
            sta for sta in ptcData.StationList if sta.ID == item["FromStationID"]][0]
        segment.ToStation = [
            sta for sta in ptcData.StationList if sta.ID == item["ToStationID"]][0]
        segment.Name = item["Name"]
        
        segment.RulerDict = dict()
        segment.HeadwayDict = dict()

        ptcData.SegmentList.append(segment)
    f.close()

    # 读取标尺数据
    f = open("./InputFiles/Ruler.csv", encoding='utf-8')
    for item in csv.DictReader(f):
        segment = [seg for seg in ptcData.SegmentList if seg.ID ==
                   item["SegmentID"]][0]

        ruler = Ruler()
        ruler.Segment = segment
        ruler.TrainType = item["TrainType"]
        ruler.FreeFlowRunningTime = int(item["FreeFlowRunningTime"])
        ruler.AccelerateAdd = int(item["AccelerateAdd"])
        ruler.DecelerateAdd = int(item["DecelerateAdd"])

        segment.RulerDict[ruler.TrainType] = ruler
    f.close()

    # 读取列车追踪间隔数据
    f = open("./InputFiles/Headway.csv", encoding='utf-8')
    for item in csv.DictReader(f):
        segment = [seg for seg in ptcData.SegmentList if seg.ID ==
                   item["SegmentID"]][0]

        headway = Headway()
        headway.Segment = segment
        headway.ID = item["ID"]
        headway.I_aa = int(item["I_aa"])
        headway.I_dd = int(item["I_dd"])
        headway.I_pd = int(item["I_pd"])
        headway.I_ap = int(item["I_ap"])
        headway.I_pp = int(item["I_pp"])
        headway.I_pa = int(item["I_pa"])
        headway.I_dp = int(item["I_dp"])

        segment.HeadwayDict[headway.ID] = headway
    f.close()

    # 读取列车列表
    f = open("./InputFiles/Train.csv", encoding='utf-8')
    for item in csv.DictReader(f):
        train = Train()
        train.ID = item["ID"]
        train.Type = item["Type"]
        train.RouteSegmentList = list()
        train.RouteStationList = list()
        train.OriginalTrainEventList = list()
        train.CompressedTrainEventList = list()

        for segmentID in item["Route"].split("-"):
            segment = [
                seg for seg in ptcData.SegmentList if seg.ID == segmentID][0]
            train.RouteSegmentList.append(segment)

        ptcData.TrainList.append(train)
    f.close()

    # 读取列车时刻表
    f = open("./InputFiles/OrgTimetable.csv", encoding='utf-8')
    for item in csv.DictReader(f):
        train = [tr for tr in ptcData.TrainList if tr.ID == item["TrainID"]][0]
        station = [sta for sta in ptcData.StationList if sta.ID ==
                   item["StationID"]][0]

        train.RouteStationList.append(station)

        trainEvent = TrainEvent()
        trainEvent.Train = train
        trainEvent.Station = station
        trainEvent.Type = "Arrival"
        trainEvent.Time = ConvertTimeToTotalMinute(item["ArrivalTime"])
        train.OriginalTrainEventList.append(trainEvent)

        trainEvent = TrainEvent()
        trainEvent.Train = train
        trainEvent.Station = station
        trainEvent.Type = "Departure"
        trainEvent.Time = ConvertTimeToTotalMinute(item["DepartureTime"])
        train.OriginalTrainEventList.append(trainEvent)

    f.close()
    return ptcData


def Plot(data, filePath):
    ''' 绘制列车运行图
        先输出绘制列车运行图需要的xml文件，然后启用命令行，调用绘图程序绘制列车运行图
        绘制得到的列车运行图在命令行当前目录下的timetable.svg文件，可以使用浏览器打开查看
        绘图程序需要系统安装 Microsoft .Net 6.0 才可以正常运行，否则在调用绘图程序时会报错
        Microsoft .Net 6.0 的下载安装地址为 https://dotnet.microsoft.com/en-us/download/dotnet/6.0  
        可以根据操作系统选择对应的 .NET Runtime 下载安装
    '''

    root = Element("TimetableMetaData")
    root.attrib["LineName"] = "京哈高铁"

    stationSetEle = SubElement(root, "StationSet")
    for station in data.StationList:
        stationEle = SubElement(stationSetEle, "Station")
        stationEle.attrib["ID"] = station.ID
        stationEle.attrib["Name"] = station.Name

        stationTrackSetEle = SubElement(stationEle, "StationTracks")

    segmentSetEle = SubElement(root, "SegmentSet")
    segmentListStr = ""
    for segment in data.SegmentList:
        segmentEle = SubElement(segmentSetEle, "Segment")
        segmentEle.attrib["ID"] = segment.ID
        segmentEle.attrib["FromStationID"] = segment.FromStation.ID
        segmentEle.attrib["ToStationID"] = segment.ToStation.ID
        segmentEle.attrib["Name"] = segment.ID
        segmentListStr += segment.ID + ","

    timetableBlockSetEle = SubElement(root, "TimetableBlockSet")
    timetableBlockEle = SubElement(timetableBlockSetEle, "TimetableBlock")
    timetableBlockEle.attrib["ID"] = "1"
    timetableBlockEle.attrib["SegmentList"] = segmentListStr.strip(',')
    for station in data.StationList:
        stationEle = SubElement(timetableBlockEle, "Station")
        stationEle.attrib["ID"] = station.ID
        stationEle.attrib["Position"] = station.Miles
        stationEle.attrib["TrackVisibility"] = "False"

    timetableSetEle = SubElement(root, "TimetableSet")
    timetableEle = SubElement(timetableSetEle, "Timetable")
    timetableEle.attrib["ID"] = "0"
    timetableEle.attrib["Name"] = "京哈高铁运行图"
    timetableEle.attrib["TimetableRef"] = "0"

    SubElement(timetableEle, "BlockadeSet")
    SubElement(timetableEle, "RollingStockPathSet")
    trainSetEle = SubElement(timetableEle, "TrainSet")
    for train in data.TrainList:
        trainEle = SubElement(trainSetEle, "Train")
        trainEle.attrib["ID"] = train.ID
        trainEle.attrib["Name"] = train.ID
        trainEle.attrib["Type"] = "Passenger"
        trainEle.attrib["OriginType"] = "Departure"
        trainEle.attrib["SinkType"] = "Termination"
        trainEle.attrib["RefRunDir"] = "0"
        trainEle.attrib["RefLineName"] = ""
        trainEle.attrib["RefPreTrainID"] = ""
        trainEle.attrib["RefNextTrainID"] = ""
        trainEle.attrib["RefNextTrainName"] = ""

        routeStr = ""
        for segment in train.RouteSegmentList:
            routeStr += segment.ID + ","
        trainEle.attrib["Route"] = routeStr.strip(',')

        eventListEle = SubElement(trainEle, "Timetable")
        for event in train.CompressedTrainEventList:
            eventEle = SubElement(eventListEle, "Event")
            eventEle.attrib["StationID"] = event.Station.ID
            eventEle.attrib["Type"] = event.Type
            HH = int(event.Time / 60)
            mm = event.Time - HH * 60

            eventEle.attrib["Time"] = str(datetime.time(HH, mm, 00))
            eventEle.attrib["Track"] = ""

    tree = ElementTree(root)
    tree.write(filePath, encoding="utf-8")

    os.system(
        "dotnet ./TimetableSvgDrawer/KappaRail.TimetableSvgDrawer.dll ./OutputFiles/TimetableMetaData.xml")
